/*
 * Created by SharpDevelop.
 * User: MyWife
 * Date: 10/12/2009
 * Time: 11:14 PM
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */

using System;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using popLib;
using System.Threading;
using System.Data;
using System.Diagnostics;
using System.Windows.Forms;
using System.Text;

namespace FileTransferSocket
{
	/// <summary>
	/// Description of Global.
	/// </summary>
	public class Global
	{
	
		static Global _global;
				
		public string SavePath = "";
        public int SocketBufferSize = 10 * 1024 * 1024;
        public int TransferBufferSize = 32767;

        public enum TranferStatus { Begin, Transferring, End, Error }
        public delegate void TransferStatusEvent(string fileName, long fileSize, long transferedSize, TranferStatus status);

        public event TransferStatusEvent ReceiveFileStatus;
        public event TransferStatusEvent SendFileStatus;
        

        public static Global Singleton()
        {
            if (_global == null) _global = new Global();
            return _global;
        }

        public void handleStream(Socket socket, NetworkStream stream)
        {

            if (!stream.CanRead) return;

            string fileName = "";
            long DataLen = 0;
            bool readingInfo = true;

            long getted = 0;
            byte[] buffer = new byte[0], storeByte = new byte[0];


            try
            {

                while (readingInfo)
                {
                    if (!stream.CanRead) continue;
                    buffer = new byte[socket.Available > TransferBufferSize ? TransferBufferSize : socket.Available];                   
                    stream.Read(buffer, 0, buffer.Length);

                    try
                    {
                        storeByte = MergeArray(storeByte, buffer);

                        if (storeByte.Length < 8) continue;

                        byte[] defineByte = ToArray(storeByte, 0, 1);
                        byte[] filenameLenByte = ToArray(storeByte, 1, 4);
                        byte[] filedataLenByte = ToArray(storeByte, 5, 4);


                        int filenameLen = BitConverter.ToInt32(filenameLenByte, 0);
                        int filedataLen = BitConverter.ToInt32(filedataLenByte, 0);

                        if (storeByte.Length < 9 + filenameLen || filenameLen < 0) continue;
                        fileName = Encoding.ASCII.GetString(storeByte, 9, filenameLen);

                        if (storeByte.Length < 9 + filenameLen + filedataLen) continue;
                        filedataLenByte = ToArray(storeByte, 9 + filenameLen, filedataLen);
                        DataLen = BitConverter.ToInt64(filedataLenByte, 0);

                        if (ReceiveFileStatus != null) ReceiveFileStatus(fileName, DataLen, getted, TranferStatus.Begin);

                        storeByte = ToArray(storeByte, 9 + filenameLen + filedataLen, storeByte.Length - (9 + filenameLen + filedataLen));

                        string filesave = StringLib.isValid(SavePath) ? Path.Combine(SavePath, fileName) : FileLib.getFullPath(fileName);
                        FileStream filestream = new FileStream(filesave, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Write);
                        filestream.Write(storeByte, 0, storeByte.Length);
                        filestream.Flush();
                        getted += storeByte.Length;
                        readingInfo = false;

                        if (ReceiveFileStatus != null) ReceiveFileStatus(fileName, DataLen, getted, TranferStatus.Transferring);

                        while (getted < DataLen)
                        {
                            buffer = new byte[socket.Available > TransferBufferSize ? TransferBufferSize : socket.Available];

                            if (buffer.Length > (DataLen - getted))
                                buffer = new byte[DataLen - getted];


                            stream.Read(buffer, 0, buffer.Length);
                            filestream.Write(buffer, 0, buffer.Length);
                            filestream.Flush();
                            getted += buffer.Length;

                            if (ReceiveFileStatus != null) ReceiveFileStatus(fileName, DataLen, getted, TranferStatus.Transferring);
                        }

                        filestream.Close();

                        if (ReceiveFileStatus != null) ReceiveFileStatus(fileName, DataLen, getted, TranferStatus.End);

                    }
                    catch (Exception ex)
                    {
                        if (ReceiveFileStatus != null) ReceiveFileStatus(fileName, DataLen, getted, TranferStatus.Error);
                        FileLib.writeLog(FileLib.getFullPath("FileLog.txt"), ex, "handleStream.>>>>");
                        FormLib.alert(ex);
                    }
                }
            }
            catch (Exception ex)
            {
                FileLib.writeLog(FileLib.getFullPath("FileLog.txt"), ex, "handleStream");
                FormLib.alert(ex);
            }
        }

        public byte[] ToArray(byte[] bytes, int offset, int count)
        {
            byte[] result = new byte[count];
            for (int i = 0; i < count; i++)
                result[i] = bytes[offset + i];
            return result;
        }

        public byte[] MergeArray(byte[] arr1, byte[] arr2)
        {
            List<byte> tmp = new List<byte>();
            tmp.AddRange(arr1);
            tmp.AddRange(arr2);

            byte[] result = new byte[tmp.Count];
            tmp.CopyTo(result);
            return result;
        }


        public void sendFile(string filePath, int currentFileIndex, int totalFiles, Socket socket)
        {
            byte[] infoByte = packageFileInfo(filePath);
            //Auto send
            //socket.Send(infoByte, SocketFlags.Partial);
            //socket.SendFile(filePath);
            //socket.SendFile(filePath, infoByte, null, TransmitFileOptions.UseDefaultWorkerThread);

            //Send manually
            //*

            FileStream filestream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
            string fileName = Path.GetFileName(filePath);
            long sended = 0;
            long totalLength = filestream.Length + infoByte.Length;

            try
            {

                if (SendFileStatus != null) SendFileStatus(fileName, totalLength, sended, TranferStatus.Begin);

                byte[] buffer = new byte[TransferBufferSize];
                if (TransferBufferSize > totalLength)
                    buffer = new byte[totalLength];

                byte[] tmp = new byte[buffer.Length - infoByte.Length];
                filestream.Read(tmp, 0, tmp.Length);

                infoByte.CopyTo(buffer, 0);
                tmp.CopyTo(buffer, infoByte.Length);

                socket.Send(buffer, SocketFlags.Partial);
                sended += buffer.Length;

                if (SendFileStatus != null) SendFileStatus(fileName, totalLength, sended, TranferStatus.Transferring);

                while (sended < totalLength)
                {
                    if (TransferBufferSize > totalLength - sended)
                        buffer = new byte[totalLength - sended];
                    else buffer = new byte[TransferBufferSize];

                    filestream.Read(buffer, 0, buffer.Length);
                    socket.Send(buffer, SocketFlags.Partial);
                    sended += buffer.Length;

                    if (SendFileStatus != null) SendFileStatus(fileName, totalLength, sended, TranferStatus.Transferring);
                }

                filestream.Close();

                if (SendFileStatus != null) SendFileStatus(fileName, totalLength, sended, TranferStatus.End);

            }catch(Exception ex){
                if (SendFileStatus != null) SendFileStatus(fileName, totalLength, sended, TranferStatus.Error);
            }
            //*/            
        }

        /// [1] [4] [4] [X] [Y] [Z]
        /// [1]: 1byte for define type: 0x01 for sending file, 0x02 for sending message
        /// [4]: first 4 bytes for length of file's name in byte
        /// [4]: next 4 bytes for length of (file's data length in byte)'s length
        /// [X]: file's name in byte
        /// [Y]: length of file's data in byte
        /// [Z]: file data in byte
        public byte[] packageFileInfo(string filePath)
        {            
            byte[] defineByte = new byte[] { 0x01 };

            string fileName = Path.GetFileName(filePath);
            byte[] fileNameByte = Encoding.ASCII.GetBytes(fileName);
            byte[] fileNameByteLenByte = BitConverter.GetBytes(fileNameByte.Length);

            long dataLen = new FileInfo(filePath).Length;
            byte[] dataLenByte = BitConverter.GetBytes(dataLen);
            byte[] dataLenByteLenByte = BitConverter.GetBytes(dataLenByte.Length);

            byte[] transferData = new byte[1 + 4 + 4 + fileNameByte.Length + dataLenByte.Length];

            defineByte.CopyTo(transferData, 0);
            fileNameByteLenByte.CopyTo(transferData, 1);
            dataLenByteLenByte.CopyTo(transferData, 1 + 4);

            fileNameByte.CopyTo(transferData, 1 + 4 + 4);
            dataLenByte.CopyTo(transferData, 1 + 4 + 4 + fileNameByte.Length);

            return transferData;
        }
	}
}
